/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/top_background_gradient.h"

#include "apiwrap.h"
#include "api/api_peer_colors.h"
#include "data/data_emoji_statuses.h"
#include "data/data_credits.h"
#include "data/data_peer.h"
#include "data/data_star_gift.h"
#include "data/stickers/data_custom_emoji.h"
#include "main/main_session.h"
#include "ui/image/image_prepare.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/text/custom_emoji_helper.h"
#include "styles/style_layers.h"
#include "styles/style_chat_helpers.h"

namespace Ui {
namespace {

void PrepareImage(
		QImage &image,
		not_null<Ui::Text::CustomEmoji*> emoji,
		const PatternPoint &point,
		const QColor &patternColor) {
	if (!image.isNull() || !emoji->ready()) {
		return;
	}
	const auto ratio = style::DevicePixelRatio();
	const auto size = Emoji::GetSizeNormal() / ratio;
	image = QImage(
		2 * QSize(size, size) * ratio,
		QImage::Format_ARGB32_Premultiplied);
	image.setDevicePixelRatio(ratio);
	image.fill(Qt::transparent);
	auto p = QPainter(&image);
	auto hq = PainterHighQualityEnabler(p);
	p.setOpacity(point.opacity);
	if (point.scale < 1.) {
		p.translate(size, size);
		p.scale(point.scale, point.scale);
		p.translate(-size, -size);
	}
	const auto shift = (2 * size - (Emoji::GetSizeLarge() / ratio)) / 2;
	emoji->paint(p, {
		.textColor = patternColor,
		.position = QPoint(shift, shift),
	});
}

} // namespace

QImage CreateTopBgGradient(
		QSize size,
		QColor centerColor,
		QColor edgeColor,
		bool rounded,
		QPoint offset) {
	const auto ratio = style::DevicePixelRatio();
	auto result = QImage(size * ratio, QImage::Format_ARGB32_Premultiplied);
	if (!rounded) {
		result.fill(Qt::transparent);
	}
	result.setDevicePixelRatio(ratio);

	auto p = QPainter(&result);
	auto hq = PainterHighQualityEnabler(p);
	auto gradient = QRadialGradient(
		rect::center(QRect(offset, size)),
		size.height() / 2);
	gradient.setStops({
		{ 0., centerColor },
		{ 1., edgeColor },
	});
	p.setBrush(gradient);
	p.setPen(Qt::NoPen);
	p.drawRect(Rect(size));
	p.end();

	if (rounded) {
		const auto mask = Images::CornersMask(st::boxRadius);
		return Images::Round(std::move(result), mask, RectPart::FullTop);
	}
	return result;
}

QImage CreateTopBgGradient(QSize size, const Data::UniqueGift &gift) {
	return CreateTopBgGradient(
		size,
		gift.backdrop.centerColor,
		gift.backdrop.edgeColor);
}

QImage CreateTopBgGradient(
		QSize size,
		not_null<PeerData*> peer,
		QPoint offset) {
	if (const auto collectible = peer->emojiStatusId().collectible) {
		return CreateTopBgGradient(
			size,
			collectible->centerColor,
			collectible->edgeColor,
			false,
			offset);
	}
	if (const auto color = peer->session().api().peerColors().colorProfileFor(
			peer)) {
		if (color->bg.size() > 1) {
			return CreateTopBgGradient(
				size,
				color->bg[1],
				color->bg[0],
				false,
				offset);
		}
	}
	return QImage();
}

const std::vector<PatternPoint> &PatternBgPoints() {
	static const auto kSmall = 0.7;
	static const auto kFaded = 0.2;
	static const auto kLarge = 0.85;
	static const auto kOpaque = 0.3;
	static const auto result = std::vector<PatternPoint>{
		{ { 0.5, 0.066 }, kSmall, kFaded },

		{ { 0.177, 0.168 }, kSmall, kFaded },
		{ { 0.822, 0.168 }, kSmall, kFaded },

		{ { 0.37, 0.168 }, kLarge, kOpaque },
		{ { 0.63, 0.168 }, kLarge, kOpaque },

		{ { 0.277, 0.308 }, kSmall, kOpaque },
		{ { 0.723, 0.308 }, kSmall, kOpaque },

		{ { 0.13, 0.42 }, kSmall, kFaded },
		{ { 0.87, 0.42 }, kSmall, kFaded },

		{ { 0.27, 0.533 }, kLarge, kOpaque },
		{ { 0.73, 0.533 }, kLarge, kOpaque },

		{ { 0.2, 0.73 }, kSmall, kFaded },
		{ { 0.8, 0.73 }, kSmall, kFaded },

		{ { 0.302, 0.825 }, kLarge, kOpaque },
		{ { 0.698, 0.825 }, kLarge, kOpaque },

		{ { 0.5, 0.876 }, kLarge, kFaded },

		{ { 0.144, 0.936 }, kSmall, kFaded },
		{ { 0.856, 0.936 }, kSmall, kFaded },
	};
	return result;
}

const std::vector<PatternPoint> &PatternBgPointsSmall() {
	static const auto kSmall = 0.45;
	static const auto kFaded = 0.2;
	static const auto kLarge = 0.55;
	static const auto kOpaque = 0.3;
	static const auto result = std::vector<PatternPoint>{
		{ { 0.5, 0.066 }, kSmall, kFaded },

		{ { 0.177, 0.168 }, kSmall, kFaded },
		{ { 0.822, 0.168 }, kSmall, kFaded },

		{ { 0.37, 0.168 }, kLarge, kOpaque },
		{ { 0.63, 0.168 }, kLarge, kOpaque },

		{ { 0.277, 0.308 }, kSmall, kOpaque },
		{ { 0.723, 0.308 }, kSmall, kOpaque },

		{ { 0.13, 0.42 }, kSmall, kFaded },
		{ { 0.87, 0.42 }, kSmall, kFaded },

		{ { 0.27, 0.533 }, kLarge, kOpaque },
		{ { 0.73, 0.533 }, kLarge, kOpaque },

		{ { 0.2, 0.73 }, kSmall, kFaded },
		{ { 0.8, 0.73 }, kSmall, kFaded },

		{ { 0.302, 0.825 }, kLarge, kOpaque },
		{ { 0.698, 0.825 }, kLarge, kOpaque },

		{ { 0.5, 0.876 }, kLarge, kFaded },

		{ { 0.144, 0.936 }, kSmall, kFaded },
		{ { 0.856, 0.936 }, kSmall, kFaded },
	};
	return result;
}

void PaintBgPoints(
		QPainter &p,
		const std::vector<PatternPoint> &points,
		base::flat_map<float64, QImage> &cache,
		not_null<Ui::Text::CustomEmoji*> emoji,
		const Data::UniqueGift &gift,
		const QRect &rect,
		float64 shown) {
	PaintBgPoints(
		p,
		points,
		cache,
		emoji,
		gift.backdrop.patternColor,
		rect,
		shown);
}

void PaintBgPoints(
		QPainter &p,
		const std::vector<PatternPoint> &points,
		base::flat_map<float64, QImage> &cache,
		not_null<Ui::Text::CustomEmoji*> emoji,
		const QColor &patternColor,
		const QRect &rect,
		float64 shown) {
	const auto origin = rect.topLeft();
	const auto width = rect.width();
	const auto height = rect.height();
	const auto ratio = style::DevicePixelRatio();
	const auto paintPoint = [&](const PatternPoint &point) {
		const auto key = (1. + point.opacity) * 10. + point.scale;
		auto &image = cache[key];
		PrepareImage(image, emoji, point, patternColor);
		if (!image.isNull()) {
			const auto position = origin + QPoint(
				int(point.position.x() * width),
				int(point.position.y() * height));
			if (shown < 1.) {
				p.save();
				p.translate(position);
				p.scale(shown, shown);
				p.translate(-position);
			}
			const auto size = image.size() / ratio;
			p.drawImage(
				position - QPoint(size.width() / 2, size.height() / 2),
				image);
			if (shown < 1.) {
				p.restore();
			}
		}
	};
	for (const auto &point : points) {
		paintPoint(point);
	}
}

} // namespace Ui
